跳到主要内容

Shell 逻辑判断

if 语句

最基本的结构化命令就是 if-then 语句。if-then 语句有如下格式。

if command
then
commands
fi

如果写成一行要使用分号结尾

if [ -ne main_test.go ]; then echo "package main \n import \"testing\" \n func Test_StartMain(t *testing.T) { main() }">main_test.go; fi

如果你在用其他编程语言的 if-then 语句,这种形式可能会让你有点困惑。在其他编程语言中,if 语句之后的对象是一个等式,这个等式的求值结果为 TRUE 或 FALSE。但 bash shell 的 if 语句并不是这么做的。

bash shell 的 if 语句会运行 if 后面的那个命令。如果该命令的退出状态码是 0(该命令成功运行),位于 then 部分的命令就会被执行。 如果该命令的退出状态码是其他值,then 部分的命令就不会被执行,bash shell 会继续执行脚本中的下一个命令。fi 语句用来表示 if-then 语句到此结束。

if 语句语法格式:

if condition
then
command1
command2
...
commandN
fi

if else 语法格式:

if condition
then
command1
command2
...
commandN
else
command
fi

if else-if else 语法格式:

if condition1
then
command1
elif condition2
then
command2
else
commandN
fi

case ... esac

case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号 ) 开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。

可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

case ... esac 语法格式如下:

case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac

case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;

取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。

如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

下面的脚本提示输入 1 到 4,与每一种模式进行匹配:

echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac

输入不同的内容,会有不同的结果,例如:

输入 1 到 4 之间的数字:
你输入的数字为:
3
你选择了 3

test 命令 ⭐

上面介绍了 if-then 语句后面检查的是语句的返回值是否为 0 来判断是否执行,所以有时这种靠状态码的方式并不方便,所以 bash shell 提供了一个 test 命令用于检查某个条件是否成立

如果 test 命令中列出的条件成立,test 命令就会退出并返回退出状态码 0。这样 if-then 语句就与其他编程语言中的 if-then 语句以类似的方式工作了。如果条件不成立,test 命令就会退出并返回非零的退出状态码,这使得 if-then 语句不会再被执行。

它可以进行数值、字符和文件三个方面的测试。

使用例

#!/bin/bash

my_variable="Full"
#
if test $my_variable
then
echo "The $my_variable expression returns a True"
#
else
echo "The $my_variable expression returns a False"
fi

输出:

The Full expression returns a True

bash shell 提供了另一种条件测试方法,无需在 if-then 语句中声明 test 命令。

if [ condition ]
then
commands
fi

方括号定义了测试条件,是与 test 命令同义的特殊 bash 命令。注意,第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错。

例如上面的例子就可以改成

if [ $my_variable ]
# ...

数值比较

参数说明
-eq等于则为真
-ne不等于则为真
-gt大于则为真
-ge大于等于则为真
-lt小于则为真
-le小于等于则为真

使用例

num1=100
num2=100
# 注意,第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错。
if [ $num1 -eq $num2 ]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi

输出结果:

两个数相等!

字符串比较

参数说明
=等于则为真
!=不相等则为真
<比较大小
>比较大小
-z 字符串字符串的长度为零则为真
-n 字符串字符串的长度不为零则为真

记住,在比较字符串的相等性时,比较测试会将所有的标点和大小写情况都考虑在内。

#!/bin/bash
num1="alsritter"
num2="alsritter"
if [ $num1 = $num2 ]
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi

输出结果:

两个字符串不相等!

注意要测试一个字符串是否比另一个字符串大时大于号和小于号必须转义,否则 shell 会把它们当作重定向符号,把字符串值当作文件名;

所以需要使用 \>

#!/bin/bash
num1="hello"
num2="world"
if [ $num1 \> $num2 ]
then
echo 'num1 大!'
else
echo 'num2 大!'
fi

最后,-n 和-z 可以检查一个变量是否含有数据。如果一个变量为空字符串,或其从未被定义,那么均会被认为它的字符串长度为 0。

文件比较

参数说明
-e file如果文件存在则为真
-r file如果文件存在且可读则为真
-w file如果文件存在且可写则为真
-x file如果文件存在且可执行则为真
-s file如果文件存在且至少有一个字符则为真
-d file如果文件存在且为目录则为真
-f file如果文件存在且为普通文件则为真
-c file如果文件存在且为字符型特殊文件则为真
-b file如果文件存在且为块特殊文件则为真
-O file检查 file 是否存在并属当前用户所有
-G file检查 file 是否存在并且默认组与当前用户相同
file1 -nt file2检查 file1 是否比 file2 新
file1 -ot file2检查 file1 是否比 file2 旧

使用例

if [ -e test.txt ]
then
echo '文件已存在!'
else
echo '文件不存在!'
fi

输出结果:

文件已存在!

需要注意的是,-G 比较会检查文件的默认组,如果它匹配了用户的默认组,则测试成功。-G 比较只会检查默认组而非用户所属的所有组。

所以如果文件的组被改成了某个组,用户也是其中的一员,但用户并不以其为默认组,此时-G 比较会失败,因为它只比较默认组,不会去比较其他的组。

此外,在比较两个文件的新旧时,这些比较都不会先检查文件是否存在,如果你要检查的文件已经移走,就会出现问题。在尝试使用-nt 或-ot 比较文件之前,必须先确认文件是存在的。

不存在则是使用

if [ ! -e "main_test.go" ]; then echo "package main \n import \"testing\" \n func Test_StartMain(t *testing.T) { main() }">main_test.go; fi

复合条件测试

if-then 语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:

  • [ condition1 ] && [ condition2 ]
  • [ condition1 ] || [ condition2 ]

结合方括号测试方式和布尔逻辑组合,可以测试更多条件。

if 语句的高级特性

bash shell 提供了两项可在 if-then 语句中使用的高级特性:

  • 用于数学表达式的双括号
  • 用于高级字符串处理功能的双方括号

使用双括号(数学表达式)

双括号命令允许在比较过程中使用高级数学表达式。

test 命令只能在比较中使用简单的算术操作。双括号命令提供了更多的数学符号,如下列出了双括号命令中还可以使用的其他运算符。

val++ 后增
val-- 后减
++val 先增
--val 先减
! 逻辑求反
~ 位求反
** 幂运算
<< 左位移
>> 右位移
& 位布尔和
| 位布尔或
&& 逻辑和
|| 逻辑或

如下所示

#!/bin/bash
# using double parenthesis
#
val1=10

# 这里 "$val1" 使用 " 包起来其实只是为了方便区分变量,去掉也可以
if (( "$val1" ** 2 > 90 ))
then
(( val2 = "$val1" ** 2 ))
echo "The square of $val1 is $val2"
fi

注意,不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性。

使用双方括号(字符串比较)

双方括号命令提供了针对字符串比较的高级特性。双方括号使用了 test 命令中采用的标准字符串比较。但它提供了 test 命令未提供的另一个特性——模式匹配(pattern matching)。

双方括号在 bash shell 中工作良好。不过要小心,不是所有的 shell 都支持双方括号。

在模式匹配中,可以定义一个正则表达式来匹配字符串值。

#!/bin/bash
# using pattern matching
#
if [[ $USER == a* ]]
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi

References

数值比较